//
//  rovbus.c
//  YHROV-ARM_UnitTest
//
//  Created by CmST0us on 16/11/1.
//  Copyright © 2016年 广州睿航电子科技有限公司. All rights reserved.
//

#include "rovbus.h"
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#define CHAR_TO_HEX(x)  if ((x) >= '0' && (x) <= '9') { \
                            (x) -= 48; \
                        } \
                        if ((x) >= 'A' && (x) <= 'F') { \
                            (x) -= 55; \
                        } \
                        if ((x) >= 'a' && (x) <= 'f') { \
                            (x) -= 87; \
                        }

#define HEX_TO_CHAR(x)  if ((x) >= 10) { \
                            (x) += 55; \
                        }else{ \
                            (x) += 48; \
                        }

rovbus_ref_t rovbus_new(){
    rovbus_ref_t r = (rovbus_ref_t)malloc(sizeof(rovbus_t));
    assert(r != NULL);
    r->_start = ':';
    r->_data_count = 0;
    r->_end[0] = '\r';
    r->_end[1] = '\n';
    return r;
}

void rovbus_free(rovbus_ref_t r){
    free(r->_data);
    free(r);
}
void rovbus_set_address(rovbus_ref_t r, rovbus_address_t addr){
    r->_address[0] = addr[0];
    r->_address[1] = addr[1];
}
void rovbus_set_fuction(rovbus_ref_t r, rovbus_fuction_t function){
    r->_function[0] = function[0];
    r->_function[1] = function[1];
}
int rovbus_set_data(rovbus_ref_t r,const void* buffer, int len){
    //分离字符
    int i;
    char c;
    char hc;
    char lc;
    r->_data = (unsigned char*)malloc(len * 2);
    r->_data_count = len;
    memset(r->_data, 0, len * 2);
    for (i = 0; i < len; ++i) {
        c = *((char*)buffer + i);
        hc = (c >> 4) & 0x0F;
        lc = c & 0x0F;
        HEX_TO_CHAR(hc);
        HEX_TO_CHAR(lc);
        memcpy(r->_data + 2 * i, &hc, 1);
        memcpy(r->_data + 2 * i + 1, &lc, 1);
    }
    return 0;
}
void rovbus_compute_lrc(rovbus_ref_t r){
    int sum = 0;
    char lc;
    char hc;
    char c;
    hc = r->_address[0];
    lc = r->_address[1];
    CHAR_TO_HEX(hc);
    CHAR_TO_HEX(lc);
    c = (hc << 4) | lc;
    sum += c;
    hc = r->_function[0];
    lc = r->_function[1];
    CHAR_TO_HEX(hc);
    CHAR_TO_HEX(lc);
    c = (hc << 4) | lc;
    sum += c;
    for (int i = 0; i < r->_data_count; ++i) {
        hc = *(char*)(r->_data + 2 * i);
        lc = *(char*)(r->_data + 2 * i + 1);
        CHAR_TO_HEX(hc);
        CHAR_TO_HEX(lc);
        c = (hc << 4) | lc;
        sum += c;
    }
    char lrc = ~(sum % 256) + 1;
    char hlrc;
    char llrc;
    hlrc= (lrc >> 4) & 0x0F;
    llrc = lrc & 0x0F;
    HEX_TO_CHAR(hlrc);
    HEX_TO_CHAR(llrc);
    r->_lrc[0] = hlrc;
    r->_lrc[1] = llrc;
}
int rovbus_check_lrc(const rovbus_ref_t r){
    int sum = 0;
    char lc;
    char hc;
    char c;
    hc = r->_address[0];
    lc = r->_address[1];
    CHAR_TO_HEX(hc);
    CHAR_TO_HEX(lc);
    c = (hc << 4) | lc;
    sum += c;
    hc = r->_function[0];
    lc = r->_function[1];
    CHAR_TO_HEX(hc);
    CHAR_TO_HEX(lc);
    c = (hc << 4) | lc;
    sum += c;
    for (int i = 0; i < r->_data_count; ++i) {
        hc = *((char*)r->_data + 2 * i);
        lc = *((char*)r->_data + 2 * i + 1);
        CHAR_TO_HEX(hc);
        CHAR_TO_HEX(lc);
        c = (hc << 4) | lc;
        sum += c;
    }
    char lrc = ~(sum % 256) + 1;
    char hlrc;
    char llrc;
    hlrc= (lrc >> 4) & 0x0F;
    llrc = lrc & 0x0F;
    HEX_TO_CHAR(hlrc);
    HEX_TO_CHAR(llrc);
    if (r->_lrc[0] == hlrc && r->_lrc[1] == llrc){
        return 0;
    }
    return -1;
}
int rovbus_check_legal(const rovbus_ref_t r){
    if (r->_start == ':' && r->_end[0] == '\r' && r->_end[1] == '\n') {
        return 0;
    }
    return -1;
}
rovbus_ref_t rovbus_init_with_string(const void* string, int len){
    if (len < 9) {
        return NULL;
    }
    
    rovbus_ref_t t = (rovbus_ref_t)malloc(sizeof(rovbus_t));
    memset(t, 0, sizeof(rovbus_t));
    char *cData = (char*)string;
    t->_start = cData[0];
    t->_address[0] = cData[1];
    t->_address[1] = cData[2];
    t->_function[0] = cData[3];
    t->_function[1] = cData[4];
    t->_end[1] = cData[len - 1];
    t->_end[0] = cData[len - 2];
    t->_lrc[1] = cData[len - 3];
    t->_lrc[0] = cData[len - 4];
    t->_data_count = (len - 9) / 2;
    t->_data = (unsigned char*)malloc(len - 9);
    memset(t->_data, 0, len - 9);
    memcpy(t->_data, cData + 5, len - 9);
    return t;
}
int rovbus_get_data(rovbus_ref_t r, void* buffer, int len){
    int data_len = 0;
    char *cData = (char*)buffer;
    for (; data_len < r->_data_count; ++data_len) {
        if (data_len == len) {
            break;
        }
        char hc;
        char lc;
        char c;
        hc = *((char*)r->_data + 2 * data_len);
        lc = *((char*)r->_data + 2 * data_len + 1);
        CHAR_TO_HEX(hc);
        CHAR_TO_HEX(lc);
        c = (hc << 4) | lc;
        cData[data_len] = c;
    }
    return data_len;
}
int rovbus_get_protocol_string(const rovbus_ref_t r, void *buffer, int len){
    if (!(rovbus_check_legal(r) == 0 && rovbus_check_lrc(r) == 0)) {
        return -1;
    }
    if (len < 9 + 2 * r->_data_count) {
        return -2;
    }
    char* cData = (char*)buffer;
    cData[0] = r->_start;
    cData[1] = r->_address[0];
    cData[2] = r->_address[1];
    cData[3] = r->_function[0];
    cData[4] = r->_function[1];
    for (int i = 0; i < r->_data_count * 2; ++i) {
        cData[5 + i] = r->_data[i];
    }
    cData[5 + 2 * r->_data_count] = r->_lrc[0];
    cData[5 + 2 * r->_data_count + 1] = r->_lrc[1];
    cData[5 + 2 * r->_data_count + 2] = r->_end[0];
    cData[5 + 2 * r->_data_count + 3] = r->_end[1];
    return 9 + 2 * r->_data_count;
}
char rovbus_get_address(const rovbus_ref_t r, int i) {
    if (i < 0 || i > 1) {
        return -1;
    }
    return r->_address[i];
}
char rovbus_get_function(const rovbus_ref_t r, int i) {
    if (i < 0 || i > 1) {
        return -1;
    }
    return r->_function[i];
}

unsigned int rovbus_get_address_num(const rovbus_ref_t r) {
    char lc;
    char hc;
    hc = r->_address[0];
    lc = r->_address[1];
    CHAR_TO_HEX(hc)
    CHAR_TO_HEX(lc)
    unsigned int c = (hc << 4) | lc;
    return c;
}
unsigned int rovbus_get_function_num(const rovbus_ref_t r) {
    char lc;
    char hc;
    hc = r->_function[0];
    lc = r->_function[1];
    CHAR_TO_HEX(hc)
    CHAR_TO_HEX(lc)
    unsigned int c = (hc << 4) | lc;
    return c;
}

